//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/Resources/Texture.h"
#include "Graphics/GLshell/Shader.h"
#include "Graphics/GLshell/Sampler.h"
#include "Graphics/GLshell/RenderState.h"
#include "Graphics/GLshell/Uniform.h"

namespace Engine
{
namespace Graphics
{

	struct GraphicsConst
	{
		enum
		{
			MAX_MATERIAL_TEXTURES	= 4,
		};
	};

	
	class Program;
	SHARED_POINTER( Program );



	//
	// Program
	//

	class Program : public Resource
	{
		friend struct ProgramLoader;
		friend class  ShaderManager;

	// types
	public:

		//
		// Program Load Parameters
		//
		struct ProgramLoadParams : ResLoadParams
		{
		// types
		public:
			typedef File::SimpleRCryptFile::CryptAlgorithm::password_t	password_t;

		// variables
		public:
			const password_t	password;
			const bool			hasPassword;

		// methods
		public:
			ProgramLoadParams () :
				hasPassword(false), ResLoadParams( Signature() )
			{}

			ProgramLoadParams (uni_c_string pw) :
				hasPassword(true), password( uni_array<const uint8>( (uint8 *)pw.cstr(), pw.Length() ) ),
				ResLoadParams( Signature() )
			{}

			ProgramLoadParams (const uni_array<const uint8> &pw) :
				hasPassword(true), password(pw), ResLoadParams( Signature() )
			{}

			static signature_t Signature ()
			{
				return CompileTime::TSignature8< 'G', 'P', 'R', 'O', 'G' >::value;
			}
		};


		//
		// Program Texture
		//
		struct Texture
		{
			Uniform< sampler_t >	uniform;
			Sampler					sampler;
			bool					enabled;
			///
			Texture () : enabled(false) {}
		};


		//
		// Program Vertex Attrib
		//
		struct Attrib
		{
			string					name;
			int						index;
			gl_vertex_attrib::type	type;
			///
			Attrib () : index(-1), type(gl_vertex_attrib::NONE) {}
		};

		typedef static_array< Texture, GraphicsConst::MAX_MATERIAL_TEXTURES >		tex_array_t;

		enum {
			FRAGMENT_DATA_SIZE	= 16,
		};

		typedef UniformBuffer< vec4, FRAGMENT_DATA_SIZE >		frag_ub_t;


	// variables
	protected:
		ShaderProgram			_program;
		tex_array_t				_textures;
		uint					_numTextures;
		RenderState				_renderState;
		GLVertexAttribsInfo		_attribs;
		frag_ub_t				_fragUB;


	// methods
	private:
		void _AddAttrib (uint i, uint8 index, gl_vertex_attrib::type type);

	public:
		Program (const EngineSubSystemsRef ess);
		~Program ();

		Program &	SetRenderState (const RenderState &renderState);
		Program &	AddTexture (uni_c_string uniformName, const Sampler &samp = Sampler());

		void Bind ();
		void ApplyRenderState (const TOptional<RenderState::ColorStateSeparate> &cs);

		ShaderProgram &		GetShaderProgram ()		{ return _program; }

		tex_array_t &		GetTextures () 			{ return _textures; }

		RenderState const & GetRenderState() const	{ return _renderState; }

		frag_ub_t &			GetFragmentUB ()		{ return _fragUB; }
		

		// Resource //
		virtual bool SwapData (const ResourcePtr &) override;
		
		virtual bool IsLoadedWithParams (const ResLoadParams &params) const override;

		virtual bool IsValid () const override;


		static ProgramPtr	New (const EngineSubSystemsRef ess);

		static ProgramLoadParams	GetDefaultLoadParams ();
	};

	
	
/*
=================================================
	Bind
=================================================
*/
	inline void Program::Bind ()
	{
		ESS()->GetGLStateManager()->UseProgram( _program.Id() );
		ESS()->GetGLStateManager()->SetProgramAttribs( _attribs );

		if ( _fragUB.IsValid() )
			_fragUB.Update();
	}
	
/*
=================================================
	ApplyRenderState
=================================================
*/
	inline void Program::ApplyRenderState (const TOptional<RenderState::ColorStateSeparate> &cs)
	{
		StateManager * sm = ESS()->GetGLStateManager();

		sm->SetColorState( cs.IsDefined() ? cs.Get() : _renderState.colorState );
		sm->SetCullMode( _renderState.cullMode );
		sm->SetDepthState( _renderState.depthState );
		sm->SetStencilState( _renderState.stencilState );
		sm->SetPolygonOffset( _renderState.polygonOffset );
	}

/*
=================================================
	_AddAttrib
=================================================
*/
	inline void Program::_AddAttrib (uint i, uint8 index, gl_vertex_attrib::type type)
	{
		_attribs.Set( i, GLVertexAttribsInfo::GLAttribInfo( type, index, 1, true ) );
	}
	
/*
=================================================
	SetRenderState
=================================================
*/
	inline Program & Program::SetRenderState (const RenderState &renderState)
	{
		_renderState = renderState;
		return *this;
	}


}	// Graphics
}	// Engine
